using System;
using System.Net;
using System.Text;
using Publish.Enum;
using System.Threading;
using System.Net.Sockets;

namespace Publish.Services.SocketUdp
{
    /// <summary>
    /// udp 服务器  主要接收和返回自己的主机信息
    /// </summary>
    public class SocketUdpServer
    {
        #region Field

        /// <summary>
        /// 监听线程
        /// </summary>
        private Thread listenThread;

        /// <summary>
        /// 一个监听的socket
        /// </summary>
        private Socket listenSocket;

        /// <summary>
        /// 定义监听端口
        /// </summary>
        private int port;

        /// <summary>
        /// 收到的信息
        /// </summary>
        private String message = "";

        /// <summary>
        /// 远端的网络端点
        /// </summary>
        private EndPoint remoteEndPoint = null;

        /// <summary>
        /// 当前的静态实例
        /// </summary>
        private static SocketUdpServer socketUdpServer = null;

        #endregion

        #region Delegate Event

        #region OnCompleteHander

        /// <summary>
        /// 完成一个socket的代理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void OnCompleteHander(SocketUdpServer sender, EventArgs e);

        /// <summary>
        /// 完成收到一个主机信息 即上线事件
        /// </summary>
        public event OnCompleteHander OnLineComplete;
        /// <summary>
        /// 完成下线事件
        /// </summary>
        public event OnCompleteHander OfflineComplete;

        /// <summary>
        /// 完成一次谈话  就一条信息
        /// </summary>
        public event OnCompleteHander OnChatComplete;
        /// <summary>
        /// 有错误出现
        /// </summary>
        public event OnCompleteHander ErrorAppear;

        #endregion

        #region OnDatagramHander

        /// <summary>
        /// 完成一个广播代理
        /// </summary>
        /// <param name="sender"></param>
        public delegate void OnDatagramHander(Datagram sender);

        /// <summary>
        /// 进行广播
        /// </summary>
        public event OnDatagramHander DatagramComplete;

        #endregion

        #endregion

        #region Constructor

        /// <summary>
        /// 构造函数 初始化 单例 私有
        /// </summary>
        private SocketUdpServer()
        {
            port = 18180;
        }

        #endregion

        #region  Destructor

        /// <summary>
        /// 析构方法
        /// </summary>
        ~SocketUdpServer()
        {
            listenThread.Abort();
            listenSocket.Close();
        }

        #endregion

        #region Property

        /// <summary>
        /// 获取收到的信息
        /// </summary>
        public String Message { get { return message; } }

        /// <summary>
        /// 获取远端的端点
        /// </summary>
        public EndPoint RemoteEndPoint { get { return remoteEndPoint; } }

        /// <summary>
        /// 只能有一个服务器 所以该类只能从此处去实例化
        /// </summary>
        public static SocketUdpServer Instance
        {
            get { return socketUdpServer ?? (socketUdpServer = new SocketUdpServer()); }
        }

        #endregion

        #region Method

        #region 停止当前监听和断开线程

        /// <summary>
        /// 停止当前服务器的监听和断开线程
        /// </summary>
        public void Stop()
        {
            listenThread.Abort();
            listenSocket.Close();
        }

        #endregion

        #region 监听

        /// <summary>
        /// 开始监听
        /// </summary>
        public void Listen()
        {
            listenThread = new Thread(() => ListenMethod()) { IsBackground = true };
            listenThread.Start();
        }

        /// <summary>
        /// 监听的方法
        /// </summary>
        private void ListenMethod()
        {
            try
            {
                listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                var ipep = new IPEndPoint(IPAddress.Any, port);
                listenSocket.Bind(ipep); //定义一个网络端点

                var sender = new IPEndPoint(IPAddress.Any, 0); //定义要发送的计算机的地址
                var remote = (EndPoint) (sender); //远程

                //持续监听
                while (true)
                {
                    var data = new byte[1024];

                    //准备接收
                    int recv = listenSocket.ReceiveFrom(data, ref remote);
                    var stringData = Encoding.UTF8.GetString(data, 0, recv);
                    //将接收到的信息转化为自定义的数据报类
                    var recvicedataGram = Datagram.Convert(stringData);
                    message = recvicedataGram.Message;
                    var remotePoint = remote.ToString();
                    var remoteip = remotePoint.Substring(0, remotePoint.IndexOf(':'));

                    remote = new IPEndPoint(IPAddress.Parse(remoteip), port);
                    remoteEndPoint = remote;

                    Action(recvicedataGram);
                }
            }
            catch (SocketException ex)
            {
                
            }
            catch (ThreadAbortException ex)//监听线程终止事件
            {
                //message = ex.Message;
            }
            catch (Exception ex)
            {
                message = ex.Message;
                ErrorAppear(this, new EventArgs());
            }
        }

        /// <summary>
        /// 收到数据报后的动作
        /// </summary>
        /// <param name="datagram">数据报的类型</param>
        private void Action(Datagram datagram)
        {
            switch (datagram.Type)
            {
                case DatagramType.OnLine:
                    var sendDataGram = new Datagram
                    {
                        Type = DatagramType.GiveInfo,
                        FromAddress = String.Empty,
                        ToAddress = String.Empty,
                        ProjectName = Publish.ProjectName,
                        Message = Dns.GetHostName()
                    };
                    //告诉对方自己的信息
                    listenSocket.SendTo(Encoding.UTF8.GetBytes(sendDataGram.ToString()), remoteEndPoint);
                    OnLineComplete(this, new EventArgs());
                    break;
                case DatagramType.GiveInfo:
                    //执行添加上线用户事件
                    OnLineComplete(this, new EventArgs());
                    break;
                case DatagramType.OffLine:
                    //执行用户下线事件
                    //如果是自己下线
                    if (String.CompareOrdinal(Dns.GetHostName(), message) == 0)
                    {
                        System.Windows.Forms.Application.Exit();
                    }
                    else
                    {
                        OfflineComplete(this, new EventArgs());
                    }
                    break;
                case DatagramType.Broadcast://广播
                    DatagramComplete(datagram);//触发广播事件
                    break;
                case DatagramType.Chat:
                    //得到当前要交谈的用户
                    LanInfo lanInfo = LanList.CurrentLanList.Find(x => String.CompareOrdinal(remoteEndPoint.ToString(), x.RemoteEndPoint.ToString()) == 0);
                    //如果有查询到该用户在自己这边登记过
                    if (lanInfo != null)
                    {
                        if (lanInfo.State == TalkState.Talking)
                        {
                            //正在交谈 直接打开这次窗口
                            OnChatComplete(this, new EventArgs());
                        }
                        else
                        {
                            //没有交谈 将窗口加入信息的队列
                            var messageInfo = new MessageInfo()
                            {
                                Message = message,
                                ReceiveTime = DateTime.Now,
                                RemoteEndPoint = remoteEndPoint
                            };
                            //QueueMessage.Add(lanInfo.Host, messageInfo);
                        }
                    }
                    break;
            }
        }

        #endregion

        #endregion
    }
}
